﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Bea.Tuxedo.ATMI;

namespace XFSale.Lib
{
    using PackItemList = List<PackItem>;

    public class PackItem : ICloneable
    {
        public int itemID;
        public string itemName;
        public int itemOrder;

        public object Clone()
        {
            PackItem item = new PackItem();
            item.itemID = itemID;
            item.itemOrder = itemOrder;

            return item;
        }
    };

    public class PackMeta
    {
        public string serviceCode;
        public string packType;
        public string packDesc;
        public string recordType;
        public PackItemList packItems;

        public PackMeta()
        {
            packItems = new PackItemList();
        }
    };

    public class Pack
    {
        public const string SERVICECODE_INTERNAL = "000000";//内部功能码

        //定义包类型
        public const string PACKTYPE_REQUEST = "R";//请求
        public const string PACKTYPE_RESPONSE = "A"; //应答
        public const string PACKTYPE_SEND = "S"; //主动发送
        public const string PACKTYPE_HEAD = "H"; //包头(不允许使用)

        //定义包记录类型
        public const string RECORDTYPE_NONE = "0"; //没有记录
        public const string RECORDTYPE_SINGLE = "1"; //单条记录
        public const string RECORDTYPE_MULTIPLE = "*"; //多条记录

        //定义包记录分隔符
        public const string PACKRECORD_SEP = "\n"; //换行分隔

        //定义包结束标志
        public const string PACKFINISH_NO = "N"; //未结束
        public const string PACKFINISH_YES = "Y"; //已结束


        public string ServiceCode
        {
            get { return _serviceCode; }
            set { _serviceCode = value; }
        }
        public string PackType
        {
            get { return _packType; }
            set { _packType = value; }
        }
        public string RecordType
        {
            get { return _recordType; }
            set { _recordType = value; }
        }
        public int RecordCount
        {
            get { return _recordCount; }
            set { _recordCount = value; }
        }
        public int PackLen
        {
            get { return _packLen; }
        }
        public int BodyLen
        {
            get { return _bodyLen; }
        }
        public int HeadLen
        {
            get { return _headLen; }
        }
        public int BaseLen
        {
            get { return _baseLen; }
        }
        public int RecordLen
        {
            get { return _recordLen; }
        }
        public PackMeta HeadMeta
        {
            get { return _headMeta; }
        }
        public PackMeta BaseMeta
        {
            get { return _baseMeta; }
        }
        public PackMeta RecordMeta
        {
            get { return _recordMeta; }
        }
        public byte[] PackData
        {
            get { return _packData; }
        }
        public void GetHeadFromBuff(TypedCArray buff)
        {
            buff.GetBytes(_packData, _headLen);
        }
        public void GetBodyFromBuff(TypedCArray buff)
        {
            buff.GetBytes(_packData, _packLen);
        }
        public void GetFromBuff(TypedCArray buff)
        {
            buff.GetBytes(_packData, _packLen);
        }
        public void PutHeadToBuff(TypedCArray buff)
        {
            buff.PutBytes(_packData, _headLen);
        }
        public void PutBodyToBuff(TypedCArray buff)
        {
            buff.PutBytes(_packData, _packLen);
        }
        public void PutToBuff(TypedCArray buff)
        {
            buff.PutBytes(_packData, _packLen);
        }
        public void Init()
        {
            //初始化包信息
            Log.LogDebug("初始化包信息");
            _InitMeta();

            //初始化包数据
            Log.LogDebug("初始化包数据");
            _InitData();

            //填充包头信息
            Log.LogDebug("填充包头信息");
            _FillHead();

        }
        public void Dispose()
        {
            //释放包数据
            Log.LogDebug("释放包数据");
            _DisposeData();

            //释放包信息
            Log.LogDebug("释放包信息");
            _DisposeMeta();
        }
        public void SetHeadByName(string name, string value)
        {
            _OpByMeta(OP_ACTION_SET, OP_PACK_HEAD, name, ref value, 0);
        }
        public void SetBaseByName(string name, string value)
        {
            _OpByMeta(OP_ACTION_SET, OP_PACK_BASE, name, ref value, 0);
        }
        public void SetRecordByName(string name, string value)
        {
            SetRecordByName(name, value, 0);
        }
        public void SetRecordByName(string name, string value, int index)
        {
            _OpByMeta(OP_ACTION_SET, OP_PACK_RECORD, name, ref value, index);
        }
        public string GetHeadByName(string name)
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_GET, OP_PACK_HEAD, name, ref value, 0);
            return value;
        }
        public string GetBaseByName(string name)
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_GET, OP_PACK_BASE, name, ref value, 0);
            return value;
        }
        public string GetRecordByName(string name)
        {
            return GetRecordByName(name, 0);
        }
        public string GetRecordByName(string name, int index)
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_GET, OP_PACK_RECORD, name, ref value, index);
            return value;
        }
        public void ChkHeadByAll()
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_CHK_ALL, OP_PACK_HEAD, string.Empty, ref value, 0);
        }
        public void ChkHeadByName(string name)
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_CHK, OP_PACK_HEAD, name, ref value, 0);
        }
        public void ChkBaseByAll()
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_CHK_ALL, OP_PACK_BASE, string.Empty, ref value, 0);
        }

        public void DefHeadByAll()
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_DEF, OP_PACK_HEAD, string.Empty, ref value, 0);
        }
        public void DefBaseByAll()
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_DEF, OP_PACK_BASE, string.Empty, ref value, 0);
        }
        public void DefRecordByAll()
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_DEF, OP_PACK_RECORD, string.Empty, ref value, 0);
        }

        public void ChkBaseByName(string name)
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_CHK, OP_PACK_BASE, name, ref value, 0);
        }
        public void ChkRecordByAll()
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_CHK_ALL, OP_PACK_RECORD, string.Empty, ref value, 0);
        }
        public void ChkRecordByName(string name)
        {
            string value = string.Empty;
            _OpByMeta(OP_ACTION_CHK, OP_PACK_RECORD, name, ref value, 0);
        }
        private void _OpByMeta(int action, int part, string name, ref string value, int index)
        {
            DictMeta dm = null;
            PackMeta pm = null;
            int pos = 0;

            switch (part)
            {
                case OP_PACK_HEAD:
                    pm = _headMeta;
                    pos = 0;
                    index = 0;
                    break;
                case OP_PACK_BASE:
                    pm = _baseMeta;
                    pos = _headLen;
                    index = 0;
                    break;
                case OP_PACK_RECORD:
                    pm = _recordMeta;
                    pos = _headLen + _baseLen + (_recordLen + PACKRECORD_SEP.Length) * index;
                    break;
                default:
                    Log.LogThrowError("不支持的访问方式");
                    break;
            }

            bool found = false;
            foreach (PackItem item in pm.packItems)
            {
                if (!App.DictMeta.ContainsKey(item.itemName.Trim().ToUpper()))
                {
                    Log.LogThrowError("数据字典不包含 {0}", item.itemName);
                }
                dm = App.DictMeta[item.itemName.Trim().ToUpper()];

                string valueTemp = value.Trim();
                if (action != OP_ACTION_SET)
                {
                    valueTemp = StringUtil.BytesToStr(_packData, pos, dm.itemLen).Trim();
                }

                //检查数据
                if ((action == OP_ACTION_CHK && item.itemName.ToUpper() == name.ToUpper()) || action == OP_ACTION_CHK_ALL)
                {
                    //检查数据项最后一位为0x00
                    if (_packData[pos + dm.itemLen - 1] != 0x00)
                    {
                        Log.LogThrowError("数据项最后一位不为0x00 {0}={1}", dm.itemName, valueTemp);
                    }

                    //检查包体长度
                    if (part == OP_PACK_HEAD && name.ToUpper() == "BODYLEN")
                    {
                        if (_bodyLen != Convert.ToInt32(valueTemp))
                        {
                            Log.LogThrowError("包体长度检查错误 bodyLen={0}", _bodyLen);
                        }
                    }

                    //验证数据规则
                    if (!string.IsNullOrEmpty(dm.itemVerify))
                    {
                        if (!Regex.IsMatch(valueTemp, dm.itemVerify, RegexOptions.IgnoreCase))
                        {
                            Log.LogThrowError("正则表达式验证错误 {0}: Value=[{1}] RegExp={2}",
                                 dm.itemName, valueTemp, dm.itemVerify.Replace("{", "{{").Replace("}", "}}"));
                        }
                    }

                    if (action == OP_ACTION_CHK && item.itemName.ToUpper() == name.ToUpper())
                    {
                        found = true;
                        break;
                    }
                }

                //填充缺省值
                if (action == OP_ACTION_DEF && string.IsNullOrEmpty(valueTemp) && !string.IsNullOrEmpty(dm.itemDefault))
                {
                    //设置数据项为默认值
                    byte[] bytesTemp = StringUtil.StrToBytes(dm.itemDefault);
                    if (bytesTemp.Length > dm.itemLen)
                    {
                        Log.LogThrowError("数据项长度不能大于{0} {1}=[{2}]",
                                dm.itemLen - 1, dm.itemName, dm.itemDefault);
                    }
                    Array.Copy(bytesTemp, 0, _packData, pos, bytesTemp.Length);
                    Log.LogVerbose("设置数据项默认值: {0}=[{1}]", dm.itemName, dm.itemDefault);
                }

                //获取或填充数据项
                if ((action == OP_ACTION_GET || action == OP_ACTION_SET) && item.itemName.ToUpper() == name.ToUpper())
                {
                    //验证数据规则
                    if (!string.IsNullOrEmpty(dm.itemVerify))
                    {
                        if (!Regex.IsMatch(valueTemp, dm.itemVerify, RegexOptions.IgnoreCase))
                        {
                            Log.LogThrowError("正则表达式验证错误 {0}: Value=[{1}] RegExp={2}",
                                 dm.itemName, valueTemp, dm.itemVerify.Replace("{", "{{").Replace("}", "}}"));
                        }
                    }

                    //获取数据项
                    if (action == OP_ACTION_GET)
                    {
                        value = valueTemp;
                        Log.LogVerbose("获取数据项: {0}={1}", dm.itemName, value);
                    }

                    //设置数据项
                    if (action == OP_ACTION_SET)
                    {
                        byte[] bytesTemp = StringUtil.StrToBytes(valueTemp);
                        if (bytesTemp.Length > dm.itemLen)
                        {
                            Log.LogThrowError("数据项长度不能大于{0}  {1}=[{2}]",
                                    dm.itemLen - 1, dm.itemName, valueTemp);
                        }
                        Array.Copy(bytesTemp, 0, _packData, pos, bytesTemp.Length);
                        Log.LogVerbose("设置数据项: {0}=[{1}]", dm.itemName, valueTemp);
                    }

                    found = true;
                    break;
                }

                pos += dm.itemLen;
            }

            if (!found && action != OP_ACTION_CHK && action != OP_ACTION_DEF)
            {
                Log.LogThrowError("数据项不正确 itemName={0}", name);
            }
        }
        private void _InitMeta()
        {
            _DisposeMeta();

            //初始化包头部信息
            Log.LogDebug("初始化包头部信息");
            string key = SERVICECODE_INTERNAL + PACKTYPE_HEAD;
            if (!App.PackMeta.ContainsKey(key.ToUpper()))
            {
                throw new Error("查找包头部信息失败 key={0}", key);
            }

            _headMeta = App.PackMeta[key.ToUpper()];
            DictMeta dict = null;
            foreach (PackItem item in _headMeta.packItems)
            {
                dict = App.DictMeta[item.itemName.Trim().ToUpper()];
                _headLen += dict.itemLen;
                Log.LogVerbose("包头部数据项: itemName=[{0}] headLen=[{1}]", item.itemName, _headLen);
            }


            //初始化包基础信息
            Log.LogDebug("初始化包基础信息");
            key = SERVICECODE_INTERNAL + PackType;
            if (!App.PackMeta.ContainsKey(key.ToUpper()))
            {
                throw new Error("查找包基础信息失败 key={0}", key);
            }

            _baseMeta = App.PackMeta[key.ToUpper()];
            foreach (PackItem item in _baseMeta.packItems)
            {
                dict = App.DictMeta[item.itemName.Trim().ToUpper()];
                _baseLen += dict.itemLen;
                Log.LogVerbose("包基础数据项: itemName=[{0}] baseLen=[{1}]", item.itemName, _baseLen);
            }

            //初始化包记录信息
            Log.LogDebug("初始化包记录信息");
            key = ServiceCode + PackType;
            if (!App.PackMeta.ContainsKey(key.ToUpper()))
            {
                throw new Error("查找包记录信息失败 key={0}", key);
            }

            _recordMeta = App.PackMeta[key.ToUpper()];
            _recordType = _recordMeta.recordType;
            foreach (PackItem item in _recordMeta.packItems)
            {
                dict = App.DictMeta[item.itemName.Trim().ToUpper()];
                _recordLen += dict.itemLen;
                Log.LogVerbose("包记录数据项: itemName=[{0}] recordLen=[{1}]", item.itemName, _recordLen);
            }


            //计算包长度
            if ((_recordType == RECORDTYPE_MULTIPLE) && (_recordCount <= 0))
            {
                throw new Error("记录个数RecordCount必须大于0");
            }

            if (_recordType == RECORDTYPE_NONE)
                _recordCount = 0;
            if (_recordType == RECORDTYPE_SINGLE)
                _recordCount = 1;
            _bodyLen = _recordLen * _recordCount + _baseLen;
            _packLen = _bodyLen + _headLen;

            Log.LogDebug("包信息: recordCount=[{0}] recordLen=[{1}] bodyLen=[{2}] packLen=[{3}]", _recordCount,
                    _recordLen, _bodyLen, _packLen);
        }
        private void _DisposeMeta()
        {
            _headMeta = null;
            _baseMeta = null;
            _recordMeta = null;

            _headLen = 0;
            _baseLen = 0;
            _recordLen = 0;
            _packLen = 0;
            _bodyLen = 0;
        }
        private void _InitData()
        {
            _DisposeData();

            _packData = new byte[_packLen];
            Array.Clear(_packData, 0, _packLen);
        }
        private void _DisposeData()
        {
            _packData = null;
        }
        private void _FillHead()
        {
            SetHeadByName("PackType", PackType);
            SetHeadByName("PackFinish", PACKFINISH_YES);
            SetHeadByName("ServiceCode", ServiceCode);
            SetHeadByName("IpAddr", "192.168.1.201");
            SetHeadByName("MacAddr", "00-50-8D-9E-0B-CB");
            SetHeadByName("SendDate", DateTime.Now.ToString("yyyyMMdd"));
            SetHeadByName("SendTime", DateTime.Now.ToString("HHmmss"));
            SetHeadByName("RecordCount", string.Format("{0:D06}", RecordCount));
            SetHeadByName("BodyLen", string.Format("{0:D06}", BodyLen));
        }

        private string _serviceCode;
        private string _packType;
        private int _recordCount;
        private string _recordType;

        private PackMeta _headMeta;
        private PackMeta _baseMeta;
        private PackMeta _recordMeta;

        private byte[] _packData;

        private int _headLen;
        private int _baseLen;
        private int _recordLen;
        private int _packLen;
        private int _bodyLen;

        private const int OP_PACK_HEAD = 0;
        private const int OP_PACK_BASE = 1;
        private const int OP_PACK_RECORD = 2;

        private const int OP_ACTION_GET = 0;
        private const int OP_ACTION_SET = 1;
        private const int OP_ACTION_CHK = 2;
        private const int OP_ACTION_CHK_ALL = 3;
        private const int OP_ACTION_DEF = 4;

        private const int OP_BY_ITEMID = 0;
        private const int OP_BY_ITEMNM = 1;
    }
}
